package zero.boot.freemarkerdemo.core.log.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import zero.boot.freemarkerdemo.core.log.annotation.AdviceInfo;
import zero.boot.freemarkerdemo.core.log.interfaces.InvokeAdvice;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

@Component
@Aspect
public class MethodInvokeAdvice {

    private List<InvokeAdvice> list;
    private zero.boot.freemarkerdemo.core.log.annotation.MethodInvokeAdvice annotation;

    @Autowired(required = false)
    public MethodInvokeAdvice(List<InvokeAdvice> list) {
        this.list = list;
    }

    /**
     * 前置通知
     *
     * @param joinPoint 切点
     */
    @Before("@annotation(zero.boot.freemarkerdemo.core.log.annotation.MethodInvokeAdvice)")
    public void beforeMethod(JoinPoint joinPoint) {
       /* String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("beforeMethod " + methodName + " and " + args);
        map = new ThreadLocal<>();*/

        String[] executeAdvice = getMethodInvokeAdviceAnnotation(joinPoint).executeAdvice();

        try {
            List<Object> args = Arrays.asList(joinPoint.getArgs());
            HttpServletRequest req = getRequest();
            if (list != null) {
                for (InvokeAdvice it : list) {
                    //AdviceInfo info = it.getClass().getAnnotation(AdviceInfo.class);
                    if (executeAdvice.length == 0) {
                        it.beforeInvoke(args, req);
                    } else {
                        AdviceInfo info = it.getClass().getAnnotation(AdviceInfo.class);
                        if (info != null) {
                            String name = info.name();
                            if (!"".equals(name)) {
                                int i = arraySearch(executeAdvice, name);
                                if (i != -1) {
                                    it.beforeInvoke(args, req);
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    /**
     * 返回通知
     *
     * @param joinPoint 切点
     * @param result    方法返回结果
     */

    @AfterReturning(value = "@annotation(zero.boot.freemarkerdemo.core.log.annotation.MethodInvokeAdvice)", argNames = "joinPoint,result", returning = "result")
    public void afterReturnMethod(JoinPoint joinPoint, Object result) {
        zero.boot.freemarkerdemo.core.log.annotation.MethodInvokeAdvice annotation = getMethodInvokeAdviceAnnotation(joinPoint);

        String[] executeAdvice = getMethodInvokeAdviceAnnotation(joinPoint).executeAdvice();

        try {
            List<Object> args = Arrays.asList(joinPoint.getArgs());
            HttpServletRequest req = getRequest();
            if (list != null) {
                for (InvokeAdvice it : list) {
                    //AdviceInfo info = it.getClass().getAnnotation(AdviceInfo.class);
                    if (executeAdvice.length == 0) {
                        it.afterInvoke(result, req);
                    } else {
                        AdviceInfo info = it.getClass().getAnnotation(AdviceInfo.class);
                        if (info != null) {
                            String name = info.name();
                            if (!"".equals(name)) {
                                int i = arraySearch(executeAdvice, name);
                                if (i != -1) {
                                    it.afterInvoke(result, req);
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 从当前的线程中 获取 request 请求信息.
     *
     * @return req HttpServletRequest
     */
    private HttpServletRequest getRequest() {
        HttpServletRequest req = null;
        HttpServletResponse resp = null;

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            ServletRequestAttributes attr = (ServletRequestAttributes) requestAttributes;
            req = attr.getRequest();
            resp = attr.getResponse();
        }
        return req;
    }

    /**
     * 获取连接点的方法注解
     *
     * @param joinPoint 连接点
     */
    private zero.boot.freemarkerdemo.core.log.annotation.MethodInvokeAdvice getMethodInvokeAdviceAnnotation(JoinPoint joinPoint) {
        zero.boot.freemarkerdemo.core.log.annotation.MethodInvokeAdvice annotation = null;
        if (joinPoint != null) {
            try {
                String name = joinPoint.getSignature().getName();
                Method method = joinPoint.getTarget().getClass().getMethod(name);
                annotation = method.getAnnotation(zero.boot.freemarkerdemo.core.log.annotation.MethodInvokeAdvice.class);

                AdviceInfo info = joinPoint.getClass().getAnnotation(AdviceInfo.class);


            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return annotation;
    }

    /**
     * 查询数组位置,如果没有查询到,返回 -1
     *
     * @param arr 要检索的数组
     * @param key 要查询的值
     * @return int -1没找到,大于等于 0 找到.
     */
    private int arraySearch(String[] arr, String key) {
        int flag = -1;
        for (int i = 0; i < arr.length; i++) {
            if (key.equals(arr[i])) {
                flag = i;
                break;
            }
        }
        return flag;
    }


    @PostConstruct
    private void afterComplete() {
        Collections.sort(list, new Comparator<InvokeAdvice>() {
            @Override
            public int compare(InvokeAdvice obj1, InvokeAdvice obj2) {
                String order1Str = obj1.getClass().getAnnotation(AdviceInfo.class).order();
                String order2Str = obj2.getClass().getAnnotation(AdviceInfo.class).order();

                int o1 = "".equals(order1Str) ? 0 : Integer.valueOf(order1Str);
                int o2 = "".equals(order2Str) ? 0 : Integer.valueOf(order2Str);

                return o1 - o2;
            }
        });
        System.out.println(list);
    }

    public static void main(String[] args) {
        int a = Integer.valueOf("");
        System.out.println(a);
    }
}
